#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include "sys/time.h"
#include <omp.h>

#define ARRAY_SIZE 10

typedef enum { false, true } bool;

void initMatrix(int initialValue);
void executeProblem1(int alterationNumber);
void executeProblem2(int alterationNumber);
void printInfo();
void spinWait(int milliseconds);


int matrice[ARRAY_SIZE][ARRAY_SIZE];
int problemNumber;
int initialValue;
int alterationNumber;
bool isParallel;

double timeStart, timeEnd, Texec;
struct timeval tp;

int main(int argc, char *argv[])
{
	if ( argc != 4 ) /* argc should be 3 for correct execution */
    {
        printf("Le programme a besoin d'arguments pour fonctionner. Veuillez recommencer selon la methode suivante.\n");

        printf("prog c p n\n");
        printf("c = Choix du probleme a executer (1 ou 2)\n");
        printf("p = La valeur initiale des elements de la matrice\n");
        printf("n = le nombre d'alterations\n");
    }
    else
    {
    	printf("Choix: %s  \n", argv[1] );
        printf("Valeur initiale: %s  \n", argv[2] );
        printf("Nombre d'alterations: %s \n \n", argv[3] );

        problemNumber = atoi(argv[1]);
        initialValue = atoi(argv[2]);
        alterationNumber = atoi(argv[3]);
        isParallel = false;
        do
        {
            initMatrix(initialValue);

            gettimeofday (&tp, NULL); // Debut du chronometre
            timeStart = (double) (tp.tv_sec) + (double) (tp.tv_usec) / 1e6;

            if(problemNumber == 1) {
                executeProblem1(alterationNumber);
            }
            else if(problemNumber == 2) {
                executeProblem2(alterationNumber);
            }
            printInfo();
            isParallel = !isParallel;
        }while(isParallel);

    }

    return 0;

}

void initMatrix(int initialValue)
{
    int i, j;
    for ( i = 0; i < ARRAY_SIZE; i++ )
    {
        for ( j = 0; j < ARRAY_SIZE; j++ )
        {
            matrice[i][j] = initialValue;
        }
    }
}

void executeProblem1(int alterationNumber)
{
    int i, j, k;
	for(k = 1; k <= alterationNumber; k++)
	{
		for (i = 0; i < ARRAY_SIZE; i++ )
		{
		    if(isParallel)
            {
                #pragma omp parallel for
                for (j = 0; j < ARRAY_SIZE; j++ )
                {
                    int tempValue = matrice[i][j];
                    matrice[i][j] = tempValue + i + j;
                    spinWait(50);
                }
		    }
		    else
            {
                for (j = 0; j < ARRAY_SIZE; j++ )
                {
                    int tempValue = matrice[i][j];
                    matrice[i][j] = tempValue + i + j;
                    spinWait(50);
                }
            }

		}
	}
}

void executeProblem2(int alterationNumber)
{
	int i, j, k;
	for(k = 1; k <= alterationNumber; k++)
	{
	    for(j = 9; j > -1; j-- )
        {
            if(isParallel)
            {
                #pragma omp parallel for
                for(i = 0; i < ARRAY_SIZE; i++ )
                {
                    if(j == 9)
                    {
                        int tempValue = matrice[i][j];
                        matrice[i][j] = tempValue + i;
                    }

                    else
                    {
                        int tempValue = matrice[i][j+1];
                        matrice[i][j] = matrice[i][j] + tempValue;
                    }
                    spinWait(50);
                }
            }
            else
            {
                for(i = 0; i < ARRAY_SIZE; i++ )
                {
                    if(j == 9)
                    {
                        int tempValue = matrice[i][j];
                        matrice[i][j] = tempValue + i;
                    }
                    else
                    {
                        int tempValue = matrice[i][j+1];
                        matrice[i][j] = matrice[i][j] + tempValue;
                    }
                    spinWait(50);
                }
            }

        }
	}
}

void spinWait(int milliseconds)
{
    struct timeval startTime;
    struct timeval endTime;
    gettimeofday(&startTime, NULL);
    do
    {
        gettimeofday(&endTime, NULL);
    } while ((endTime.tv_sec-startTime.tv_sec) * 1000000 + (endTime.tv_usec-startTime.tv_usec)<
        milliseconds * 1000);
    return;
}

void printInfo()
{
    gettimeofday (&tp, NULL); // Fin du chronometre
    timeEnd = (double) (tp.tv_sec) + (double) (tp.tv_usec) / 1e6;
    Texec = timeEnd - timeStart; //Temps d'execution en secondes

    int i, j;
    for (i = 0; i < ARRAY_SIZE; i++)
    {
        for (j = 0; j < ARRAY_SIZE; j++)
        {
            printf("%d ", matrice[i][j]);
        }
        printf("\n");
    }
    printf("Execution time : %lf\n", Texec);
}
